package minijvm.cmd;

import minijvm.clz.ClassFile;
import minijvm.engine.ExecutionResult;
import minijvm.engine.JavaObject;
import minijvm.engine.StackFrame;

import static minijvm.cmd.CommandParser.*;

/**
 * @author ChangLiang
 * @date 2020/4/30
 */
public class ComparisionCmd extends TwoOperandCmd {

    public ComparisionCmd(ClassFile clzFile, String opCode) {
        super(clzFile, opCode);
    }

    @Override
    public void execute(StackFrame frame, ExecutionResult result) {
        switch (this.opCode) {
            case if_icmpge:{
                // age -> 40
                // 40
                JavaObject jo2 = frame.getOperandStack().pop();
                // this.age
                JavaObject jo1 = frame.getOperandStack().pop();
                // if age >= 40
                if (jo1.getIntValue() >= jo2.getIntValue()) {
                    this.setJumpResult(result);
                }
                break;
            }
            case if_icmpgt:{
                // age -> 40
                // 40
                JavaObject jo2 = frame.getOperandStack().pop();
                // this.age
                JavaObject jo1 = frame.getOperandStack().pop();
                // if age > 40
                if (jo1.getIntValue() > jo2.getIntValue()) {
                    this.setJumpResult(result);
                }
                break;
            }
            case if_icmple:{
                JavaObject jo2 = frame.getOperandStack().pop();
                JavaObject jo1 = frame.getOperandStack().pop();

                if(jo1.getIntValue() <= jo2.getIntValue()){
                    this.setJumpResult(result);
                }
                break;
            }
            case goto_no_condition:{
                this.setJumpResult(result);
                break;
            }
            default:{
                throw new RuntimeException(this.opCode + "has not been implemented");
            }
        }
    }

    private void setJumpResult(ExecutionResult result) {
        int offsetFromStartCmd = this.getOffsetFromStartCmd();
        result.setNextAction(ExecutionResult.JUMP);
        result.setNextCmdOffset(offsetFromStartCmd);
    }

    private int getOffsetFromStartCmd() {
        // from java虚拟机规范
        // If the comparison succeeds, the unsigned branchbyte1 and branchbyte2
        // are used to construct a signed 16-bit offset, where the offset is calculated
        // to be (branchbyte1 << 8) | branchbyte2. Execution then proceeds at that
        // offset from the address of the opcode of this if_icmp<cond> instruction
        int index1 = this.getOprand1();
        int index2 = this.getOprand2();
        short offsetFromCurrent = (short)(index1 << 8 | index2);
        return this.getOffset() + offsetFromCurrent ;
    }
}
